#!/usr/bin/env python
# -*- coding: utf-8; -*-
"""issues 函数处理发送过来的 URL, 将模板中各筛选框元素的URL生成委托给生
成器类 CheckFilter. CheckFilter 会根据当前的请求URL参数，对各复选框
input 元素的 href 属性执行子字符串 "name=key" 的增删

筛选面板中的每个按钮都是一个超链接，点击即发送请求，并更新地址栏，所以每次
更改筛选面板中的复选框或下拉框的状态，地址栏仅仅变化一组 > name=key (添加或
删除)

"""
import datetime
import json

from django.http import JsonResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from django.utils.safestring import mark_safe
from django.urls import reverse

from utils.pagination import Pagination
from utils.encrypt import uid
from web import models
from web.forms.issues import IssuesModelForm, IssuesReplyModelForm, InviteModelForm


class CheckFilter(object):
    """ 返回某个筛选项的URL生成器, 用于模板引擎迭代 """

    # 注意 name key desc 的出现场景
    # URL查询字符串: ?name=key1&name=key2
    # 模型类定义:    name_choices = ((key1, desc1),(key2, desc2),(key3, desc3))

    def __init__(self, name, data_list, request):
        # name 为查询字符串中的name，也是模型对象的字段名
        self.name = name
        self.data_list = data_list
        self.request = request

    def __iter__(self):
        # 遍历当前字段的所有复选框，给出每个复选框的a元素href属性值
        for choice in self.data_list:
            key = str(choice[0])
            desc = choice[1]
            checked = ""
            """
            知识点：QueryDict 类 (django.http)
            1. QueryDict.__init__(self, query_string=None, mutable=False)
            2. L = querydict_obj.getlist('name')
            3. change L, (need set querydict_obj._mutable = True)
            4. querydict.setlist('name', L)
            5. query_dict.urlencode()
            """

            # 一组 (name=key) 一个复选框。从request.GET中获取被选中的复选框，
            # 返回对应的key列表(与函数 issues 处理URL一样)
            keys_selected = self.request.GET.getlist(self.name)

            # 判断当前迭代中的复选框的 key 在返回页面中的 checked 属性
            # 模板引擎渲染模板时，每次迭代的起始 keys_selected 是相同的
            if key in keys_selected:  # 对于被选中的 input 元素(name=key_N)
                checked = 'checked'  # 设置选中复选框的CSS样式，高亮"被选中了"
                keys_selected.remove(key)  # 设置该复选框的href属性，突出"下次点击取消选中"
            else:
                keys_selected.append(key)  # 未选中的复选框”点击我表示选中”

            query_dict = self.request.GET.copy()
            query_dict._mutable = True  # 允许QueryDict对象可变
            # 返回页面的复选框筛选信息被包含在 query_dict 中
            query_dict.setlist(self.name, keys_selected)

            if 'page' in query_dict:
                query_dict.pop('page')  # 重置分页

            # 将query_dict
            url_query_str = query_dict.urlencode()
            print("bcair: url_query_str: {}".format(url_query_str))

            if url_query_str:
                url = "{}?{}".format(self.request.path_info, url_query_str)
                # status=1&status=2&status=3&xx=1
            else:
                url = self.request.path_info

            tpl = '<a class="cell" href="{url}"><input type="checkbox" {checked} /><label>{desc}</label></a>'
            html = tpl.format(url=url, checked=checked, desc=desc)

            # 每次迭代时，产生一条URL查询语句，如 'status=1&status=2&issues_typy=2'
            yield mark_safe(html)


class SelectFilter(object):
    def __init__(self, name, data_list, request):
        self.name = name
        self.data_list = data_list
        self.request = request

    def __iter__(self):
        # 注意该迭代器的起始 yield 产生值(代码中出现了 3 次 yield)
        yield mark_safe(
            "<select class='select2' multiple='multiple' style='width:100%;' >"
        )

        # 有多少候选项，就迭代处多少各 option
        for item in self.data_list:
            key = str(item[0])
            text = item[1]

            selected = ""
            value_list = self.request.GET.getlist(self.name)
            if key in value_list:
                selected = 'selected'
                value_list.remove(key)
            else:
                value_list.append(key)

            query_dict = self.request.GET.copy()
            query_dict._mutable = True
            query_dict.setlist(self.name, value_list)
            if 'page' in query_dict:
                query_dict.pop('page')

            param_url = query_dict.urlencode()
            if param_url:
                url = "{}?{}".format(
                    self.request.path_info,
                    param_url)  # status=1&status=2&status=3&xx=1
            else:
                url = self.request.path_info

            html = "<option value='{url}' {selected} >{text}</option>".format(
                url=url, selected=selected, text=text)
            yield mark_safe(html)
        yield mark_safe("</select>")


# issues 函数对于GET请求，解析request.GET中的请求，在模板中
def issues(request, project_id):
    """渲染问题列表页面；处理POST表单"""

    if request.method == "GET":
        # 根据URL中的查询字符串执行数据库查询
        # 1. 处理查询字符串，获得对应的ORG查询参数字典
        #    查询字符串中的key为参数字典的key的一部分, 额外的双下划线查询结
        #    构需要根据字段来判断

        # 假设查询字符串为: status=1&status=2&issues_type=1 (省略前面的问号)
        # 该字符串指定了前端发来的issues筛选请求

        print(request.GET)
        issue_filter_fields = [
            'issues_type', 'status', 'priority', 'assign', 'attention'
        ]
        # getlist 列出 QueryDict 对象中同一 key 对应值列表
        # 以 ?status=1&status=2&issues_type=1 为例
        # request.GET.getlist('status')  =>  [1, 2]

        # QueryDict.getlist方法得到name及其对应的key_list，在filter函数中对
        # 列表使用 in 操作符取得同一name"或"的关系, 查询字符串若有多个name,
        # 则是“且”的关系
        filter_args = {}

        # 遍历 request.GET 参数(与CheckFilter不同，这里不直接处理查询字符串)，
        # 将 ORM 查询语句 filter 的关键字参数键值保存到字典中
        for field in issue_filter_fields:
            value_list = request.GET.getlist(field)  # [1,2]
            if not value_list:
                continue
            filter_args["{}__in".format(field)] = value_list
        """
        解析查询字符串得到字典
        filter_args = {
            "status__in":[1,2],
            'issues_type__in':[1,]
        }

        传入 filter 函数，拆包后等效于
        filter(status__in=[1,2],issues_type__in=[1,])
        """
        print(filter_args)

        queryset = models.Issues.objects.filter(project_id=project_id)

        # filter_args 作为实参拆包后，恰好符合ORM查询语句的参数格式
        queryset = queryset.filter(**filter_args)

        # 筛选后分页
        page_object = Pagination(current_page=request.GET.get('page'),
                                 all_count=queryset.count(),
                                 base_url=request.path_info,
                                 query_params=request.GET,
                                 per_page=8)
        issues_object_list = queryset[page_object.start:page_object.end]

        form = IssuesModelForm(request)

        project_issues_type = models.IssuesType.objects.filter(
            project_id=project_id).values_list('id', 'title')

        project_total_user = [(
            request.tracer.project.creator_id,
            request.tracer.project.creator.name,
        )]
        join_user = models.ProjectUser.objects.filter(
            project_id=project_id).values_list('user_id', 'user__name')
        project_total_user.extend(join_user)

        invite_form = InviteModelForm()
        # context 为模板提供下面的可迭代对象：
        # form: 模板中的模态框表单
        # issues_object_list: 渲染模板中 issues 页面
        # .*_filter: 渲染筛选面板
        # page_html: 分页
        context = {
            'invite_form':
            invite_form,
            'form':
            form,
            'issues_object_list':
            issues_object_list,
            'page_html':
            page_object.page_html(),
            'filter_list': [
                {
                    'title':
                    "问题类型",
                    'filter':
                    CheckFilter('issues_type', project_issues_type, request)
                },
                {
                    'title':
                    "状态",
                    'filter':
                    CheckFilter('status', models.Issues.status_choices,
                                request)
                },
                {
                    'title':
                    "优先级",
                    'filter':
                    CheckFilter('priority', models.Issues.priority_choices,
                                request)
                },
                {
                    'title': "指派者",
                    'filter': SelectFilter('assign', project_total_user,
                                           request)
                },
                {
                    'title':
                    "关注者",
                    'filter':
                    SelectFilter('attention', project_total_user, request)
                },
            ],
        }

        return render(request, 'web/issues.html', context)

    form = IssuesModelForm(request, data=request.POST)
    if form.is_valid():
        form.instance.project = request.tracer.project
        form.instance.creator = request.tracer.user
        form.save()
        return JsonResponse({'status': True})

    return JsonResponse({'status': False, 'error': form.errors})


def issues_detail(request, project_id, issues_id):
    """ 编辑问题 """
    issues_object = models.Issues.objects.filter(
        id=issues_id, project_id=project_id).first()
    form = IssuesModelForm(request, instance=issues_object)

    return render(request, 'web/issues_detail.html', {
        'form': form,
        "issues_object": issues_object
    })


@csrf_exempt
def issues_record(request, project_id, issues_id):
    """ 初始化操作记录 """

    # 判断是否可以评论和是否可以操作这个问题

    if request.method == "GET":
        reply_list = models.IssuesReply.objects.filter(
            issues_id=issues_id, issues__project=request.tracer.project)
        # 将queryset转换为json格式
        data_list = []
        for row in reply_list:
            data = {
                'id': row.id,
                'reply_type_text': row.get_reply_type_display(),
                'content': row.content,
                'creator': row.creator.name,
                'datetime': row.create_datetime.strftime("%Y-%m-%d %H:%M"),
                'parent_id': row.reply_id
            }
            data_list.append(data)

        return JsonResponse({'status': True, 'data': data_list})

    form = IssuesReplyModelForm(data=request.POST)
    if form.is_valid():
        form.instance.issues_id = issues_id
        form.instance.reply_type = 2
        form.instance.creator = request.tracer.user
        instance = form.save()
        info = {
            'id': instance.id,
            'reply_type_text': instance.get_reply_type_display(),
            'content': instance.content,
            'creator': instance.creator.name,
            'datetime': instance.create_datetime.strftime("%Y-%m-%d %H:%M"),
            'parent_id': instance.reply_id
        }

        return JsonResponse({'status': True, 'data': info})
    return JsonResponse({'status': False, 'error': form.errors})


@csrf_exempt
def issues_change(request, project_id, issues_id):
    issues_object = models.Issues.objects.filter(
        id=issues_id, project_id=project_id).first()
    # 处理包含字段改动的前端发送过来的json字符串，保存到字典中
    post_dict = json.loads(request.body.decode('utf-8'))

    # 例子
    """
    {'name': 'subject', 'value': '好饿呀sdfasdf'}
    {'name': 'subject', 'value': ''}

    {'name': 'desc', 'value': '好饿呀sdfasdf'}
    {'name': 'desc', 'value': ''}

    {'name': 'start_date', 'value': '好饿呀sdfasdf'}
    {'name': 'end_date', 'value': '好饿呀sdfasdf'}

    {'name': 'issues_type', 'value': '2'}
    {'name': 'assign', 'value': '4'}
    """
    name = post_dict.get('name')
    value = post_dict.get('value')

    # 利用 Model _meta API 获取模型对象中名字为 name 的字段实例
    # Retrieving a single field instance of a model by name
    field_object = models.Issues._meta.get_field(name)

    # 如此，可以动态的找到前端触发 change 事件的input元素对应的模型字段

    def create_reply_record(content):
        """将变更记录在 IssueReply 表中"""
        new_object = models.IssuesReply.objects.create(
            reply_type=1,
            issues=issues_object,
            content=change_record,
            creator=request.tracer.user,
        )

        new_reply_dict = {
            'id': new_object.id,
            'reply_type_text': new_object.get_reply_type_display(),
            'content': new_object.content,
            'creator': new_object.creator.name,
            'datetime': new_object.create_datetime.strftime("%Y-%m-%d %H:%M"),
            'parent_id': new_object.reply_id
        }

        return new_reply_dict

    # 1. 数据库字段更新
    # 1.1 文本
    if name in ["subject", 'desc', 'start_date', 'end_date']:
        if not value:
            if not field_object.null:
                return JsonResponse({'status': False, 'error': "您选择的值不能为空"})
            setattr(issues_object, name, None)
            issues_object.save()
            change_record = "{}更新为空".format(field_object.verbose_name)
        else:
            setattr(issues_object, name, value)
            issues_object.save()
            # 记录：xx更为了value
            change_record = "{}更新为{}".format(field_object.verbose_name, value)
        return JsonResponse({
            'status': True,
            'data': create_reply_record(change_record)
        })

    # 1.2 FK字段
    if name in ['issues_type', 'module', 'parent', 'assign']:
        # 用户选择为空
        if not value:
            # 不允许为空
            if not field_object.null:
                return JsonResponse({'status': False, 'error': "您选择的值不能为空"})
            # 允许为空
            setattr(issues_object, name, None)
            issues_object.save()
            change_record = "{}更新为空".format(field_object.verbose_name)
        else:  # 用户输入不为空
            if name == 'assign':
                # 是否是项目创建者
                if value == str(request.tracer.project.creator_id):
                    instance = request.tracer.project.creator
                else:
                    project_user_object = models.ProjectUser.objects.filter(
                        project_id=project_id, user_id=value).first()
                    if project_user_object:
                        instance = project_user_object.user
                    else:
                        instance = None
                if not instance:
                    return JsonResponse({'status': False, 'error': "您选择的值不存在"})

                setattr(issues_object, name, instance)
                issues_object.save()
                change_record = "{}更新为{}".format(
                    field_object.verbose_name, str(instance))  # value根据文本获取到内容
            else:
                # 条件判断：用户输入的值，是自己的值。
                instance = field_object.rel.model.objects.filter(
                    id=value, project_id=project_id).first()
                if not instance:
                    return JsonResponse({'status': False, 'error': "您选择的值不存在"})

                setattr(issues_object, name, instance)
                issues_object.save()
                change_record = "{}更新为{}".format(
                    field_object.verbose_name, str(instance))  # value根据文本获取到内容

        return JsonResponse({
            'status': True,
            'data': create_reply_record(change_record)
        })

    # 1.3 choices字段
    if name in ['priority', 'status', 'mode']:
        selected_text = None
        for key, text in field_object.choices:
            if str(key) == value:
                selected_text = text
        if not selected_text:
            return JsonResponse({'status': False, 'error': "您选择的值不存在"})

        setattr(issues_object, name, value)
        issues_object.save()
        change_record = "{}更新为{}".format(field_object.verbose_name,
                                         selected_text)
        return JsonResponse({
            'status': True,
            'data': create_reply_record(change_record)
        })

    # 1.4 M2M字段
    if name == "attention":
        # {"name":"attention","value":[1,2,3]}
        if not isinstance(value, list):
            return JsonResponse({'status': False, 'error': "数据格式错误"})

        if not value:
            issues_object.attention.set(value)
            issues_object.save()
            change_record = "{}更新为空".format(field_object.verbose_name)
        else:
            # values=["1","2,3,4]  ->   id是否是项目成员（参与者、创建者）
            # 获取当前项目的所有成员
            user_dict = {
                str(request.tracer.project.creator_id):
                request.tracer.project.creator.name
            }
            project_user_list = models.ProjectUser.objects.filter(
                project_id=project_id)
            for item in project_user_list:
                user_dict[str(item.user_id)] = item.user.name
            name_list = []
            for user_id in value:
                name = user_dict.get(str(user_id))
                if not name:
                    return JsonResponse({
                        'status': False,
                        'error': "用户不存在请重新设置"
                    })
                name_list.append(name)

            issues_object.attention.set(value)
            issues_object.save()
            change_record = "{}更新为{}".format(field_object.verbose_name,
                                             ",".join(name_list))

        return JsonResponse({
            'status': True,
            'data': create_reply_record(change_record)
        })

    return JsonResponse({'status': False, 'error': "无法更新"})


def invite_url(request, project_id):
    """ 生成邀请码 """

    form = InviteModelForm(data=request.POST)
    if form.is_valid():
        """
        1. 创建随机的邀请码
        2. 验证码保存到数据库
        3. 限制：只有创建者才能邀请
        """
        if request.tracer.user != request.tracer.project.creator:
            form.add_error('period', "无权创建邀请码")
            return JsonResponse({'status': False, 'error': form.errors})

        random_invite_code = uid(request.tracer.user.phone)
        form.instance.project = request.tracer.project
        form.instance.code = random_invite_code
        form.instance.creator = request.tracer.user
        form.save()

        # 将验邀请码返回给前端，前端页面上展示出来。
        url = "{scheme}://{host}{path}".format(
            scheme=request.scheme,
            host=request.get_host(),
            path=reverse('invite_join', kwargs={'code': random_invite_code}))

        return JsonResponse({'status': True, 'data': url})

    return JsonResponse({'status': False, 'error': form.errors})


def invite_join(request, code):
    """ 访问邀请码 """
    current_datetime = datetime.datetime.now()
    invite_object = models.ProjectInvite.objects.filter(code=code).first()
    if not invite_object:
        return render(request, 'web/invite_join.html', {'error': '邀请码不存在'})

    if invite_object.project.creator == request.tracer.user:
        return render(request, 'web/invite_join.html', {'error': '创建者无需再加入项目'})

    exists = models.ProjectUser.objects.filter(
        project=invite_object.project, user=request.tracer.user).exists()
    if exists:
        return render(request, 'web/invite_join.html', {'error': '已加入项目无需再加入'})

    # ####### 问题1： 最多允许的成员(要进入的项目的创建者的限制）#######
    # max_member = request.tracer.package.project_member # 当前登录用户他限制

    # 是否已过期，如果已过期则使用免费额度
    max_transaction = models.Transaction.objects.filter(
        user=invite_object.project.creator).order_by('-id').first()
    if max_transaction.package.category == 1:
        max_member = max_transaction.package.project_member
    else:
        if max_transaction.end_datetime < current_datetime:
            free_object = models.Package.objects.filter(category=1).first()
            max_member = free_object.project_member
        else:
            max_member = max_transaction.package.project_member

    # 目前所有成员(创建者&参与者）
    current_member = models.ProjectUser.objects.filter(
        project=invite_object.project).count()
    current_member = current_member + 1
    if current_member >= max_member:
        return render(request, 'web/invite_join.html',
                      {'error': '项目成员超限，请升级套餐'})

    # 邀请码是否过期？
    current_datetime = datetime.datetime.now()
    limit_datetime = invite_object.create_datetime + datetime.timedelta(
        minutes=invite_object.period)
    if current_datetime > limit_datetime:
        return render(request, 'web/invite_join.html', {'error': '邀请码已过期'})

    # 数量限制？
    if invite_object.count:
        if invite_object.use_count >= invite_object.count:
            return render(request, 'web/invite_join.html',
                          {'error': '邀请码数据已使用完'})
        invite_object.use_count += 1
        invite_object.save()

    models.ProjectUser.objects.create(user=request.tracer.user,
                                      project=invite_object.project)

    # ####### 问题2： 更新项目参与成员 #######
    invite_object.project.join_count += 1
    invite_object.project.save()

    return render(request, 'web/invite_join.html',
                  {'project': invite_object.project})
